#ifndef PBVP_DISTANCETRANSFORM_H
#define PBVP_DISTANCETRANSFORM_H

#include <QVector>
#include "dynarray.h"
#include "../data/geometrydata.h"
#include "../data/reg3dvolumedata.h"
#include "../math/Vector3.h"

namespace PBVP {
	class Cell {
	public:
		Cell() {}
		~Cell() {}

		bool hasTriangle() const {
			return (triList.size() > 0);
		}

		dynamic_array<int> triList;
	};

	/// Class to compute a SDF from a closed surface
	class DistanceTransform
	{
	public:
		/// 
		DistanceTransform(GeometryData* pGeoData);
		/// 
		~DistanceTransform();
		/// 
		void getDimension(unsigned int dim[3]) const {
			dim[0] = m_Dim[0];
			dim[1] = m_Dim[1];
			dim[2] = m_Dim[2];
		}
		/// 
		void setDimension(unsigned int dim[3]) {
			m_Dim[0] = dim[0];
			m_Dim[1] = dim[1];
			m_Dim[2] = dim[2];
		}
		/// 
		void getMinMaxExt(double minExt[3], double maxExt[3]) const {
			for (int i = 0; i < 3; i++)
			{
				minExt[i] = m_MinExt[i];
				maxExt[i] = m_MaxExt[i];
			}
		}
		/// 
		void setMinMaxExt(double minExt[3], double maxExt[3]) {
			for (int i = 0; i < 3; i++)
			{
				m_MinExt[i] = minExt[i];
				m_MaxExt[i] = maxExt[i];
			}
		}
		/// 
		void setMinMaxExt(const Vector3& minVec, const Vector3& maxVec) {
			for (int i = 0; i < 3; i++)
			{
				m_MinExt[i] = minVec[i];
				m_MaxExt[i] = maxVec[i];
			}
		}
		/// Compute signed distance function
		Reg3DVolumeData* computeSDF();

		static const float MAX_FLOAT;
		static const double TOLERANCE;
	private:
		
		GeometryData* m_pGeoData;
		Reg3DVolumeData *m_pReg3Data;

		Cell*	p_Cells;
		Vector3 *m_pTriNormals;
		
		unsigned int m_Dim[3];
		double m_MinExt[3], m_MaxExt[3];

		Vector3 getTriangleNormal(int nt) {
			return m_pTriNormals[nt];
		}

		void transform1D(int len, float f[], float d[], int parent[], float span);

		float computeNearDistance(int ix, int iy, int iz, Vector3& nearPnt);

		bool pointInTriangle(const Vector3& res, const Vector3& vert0, 
			const Vector3& vert1, const Vector3& vert2, const Vector3& norm);

		double distance2Edge(const Vector3& pnt, const Vector3& v1, const Vector3& v2, Vector3& ne);

		double distance2Triangle(const Vector3& pnt, int nt, Vector3& ne);

		void computeTriangleNormals();
		///
		bool intersectCell(const Vector3& v0, const Vector3& norm, int i, int j, int k);
		/**
		 * @return 1 if they intersect                                         
		 */
		bool TriangleCubeIntersection(int nt, const Vector3& lower, const Vector3& upper);
		
		///
		double signedDist2Plane(const Vector3& pnt, const Vector3& v0, const Vector3& norm);
		
		///
		double rayTriangleIntersection(int nt, const Vector3& begin, const Vector3& end);
		/**
		* @return -1, if point is inside, return 1 if point is outside
		*/
		int inOrOut(const dynamic_array<int>& triList, const Vector3& vert, const Vector3& nearPnt);
		/// 
		bool isNearSurface(int i, int j, int k);
		/// Compute min and max extent for the geometry data
		void init();
	};
}

#endif // DISTANCETRANSFORM_H
